/* ----------------------------------------------------------------------------

	pax.template.modifier.js Copyright (C) 2006, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */


/*
	Script: pax.template.modifier
		This PAX library contains a set of methods to be used in a PAX template
		to format template data, so that it appears uniform, eg: capitalise names.
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

	Note: 
		You should note that each method from this library is imported "locally" 
		into each template, so that they can be referenced by their short name,
		for example "pax.template.modifier._def" can be referenced by just "_def".
		All PAX examples utalise this short name facility.
		Due to this facility, you must be careful when implementing your own 
		functions, so the names don't clash. 
		To make it easier, the template system will allow overriding of the methods,
		so that they have your expected functionality. This of course explains
		why each method starts with an underscore.
		
*/


var pax = pax || {};
pax.template = pax.template || {};
pax.template.modifier = pax.template.modifier || {};

/*	Method: _def
	Returns defaultValue, if value is null or empty, otherwise value.
	
	Parameters:
		value - which value to check
		defaultValue - which value to return, if value is null or empty

	Example:
		(start code)
			<div id='pax.template.modifier.def.example1'></div>
			[:.
				var myVars = {
					currency: '$',
					value: null 
				};
				pax.template.render( "You have [" + ":= currency :" + "][" + ":= _def( value, 0) :" + "] in your pocket.", myVars, pax.$('pax.template.modifier.def.example1') );
			:]
		(end)
		This ensures you get a default value of 0, even when values are null.
		
*/
pax.template.modifier._def = function( value, defaultValue ) {
	if( value == null )return defaultValue;
	return ( value != '' )? value : defaultValue;
};


/*	Method: _caps
	Returns string with first letter in uppercase, optionally converts the rest to lowercase.
	
	Parameters:
		value - which value to use
		restLower - boolean to decide if we should convert the rest of the string to lowercase

	Example:
		(start code)
			<div id='pax.template.modifier.capitalise.example1'></div>
			[:.
				var myVars = { name: 'john' };
				pax.template.render( "Hello [" + ":= _caps( name ) :" + "]!", myVars, pax.$('pax.template.modifier.capitalise.example1') );
			:]
		(end)
		This would show "Hello John!". Notice that "john" has been capitalised.

	Example:
		(start code)
			<div id='pax.template.modifier.capitalise.example2'></div>
			[:.
				var myVars = { name: 'joHnAtHaN' };
				pax.template.render( "Hello [" + ":= _caps( name, true ) :" + "]!", myVars, pax.$('pax.template.modifier.capitalise.example2') );
			:]
		(end)
		This would show "Hello Johnathan!". Notice that "joHnAtHaN" has been capitalised, and lower cased after the first letter.

*/
pax.template.modifier._caps = function( value, restLower ) {
	if( value == null )return value;
	if( value.length > 0 ) {
		var restOfValue = ( restLower )? value.substring(1).toLowerCase() : value.substring(1);
		return value.substring(0,1).toUpperCase() + restOfValue;
	}
	else return value;
};


/*	Method: _trunc
	Returns the first *length* characters of value
	
	Parameters:
		value - which value to use
		length - how many characters to return
		
	Example:
		(start code)
			<div id='pax.template.modifier.truncate.example1'></div>
			[:.
				var myVars = { 
					account: '0123456789012',
					bsb: 6
				};
				pax.template.render( "Your BSB is: [" + ":= _trunc( account,  bsb) :" + "].", myVars, pax.$('pax.template.modifier.truncate.example1') );
			:]
		(end)
		This would show "Your BSB is: 012345.".
*/
pax.template.modifier._trunc = function( value, length ) {
	if( value == null )return value;
	if( value.length > length )return value.substr(0, length);
	else return value;
};


/*	Method: _round
	Returns the value rounded to the specifed number of places
	
	Parameters:
		value - which value to use
		placs - how many decimal places to round to
		
	Example:
		(start code)
			<div id='pax.template.modifier.round.example1'></div>
			[:.
				var myVars = { 
					value: '256.187287',
					places: 2
				};
				pax.template.render( "Rounded value is: [" + ":= _round( value, places ) :" + "]", myVars, pax.$('pax.template.modifier.round.example1') );
			:]
		(end)
		This would show "Rounded value is: 256.19".
*/
pax.template.modifier._round = function( value, places ) {
	if( value == null )return value;
	return ( Math.round( value * ( 10 * places ) ) / ( 10 * places ) );
	
};


/*	Method: _indent
	Returns string with spaces at the front, optionally set number, and what string to use as seperator.
	
	Parameters:
		value - Which value to use
		number - How many spaces to insert
		string - What string to use for indentation

	Example:
		(start code)
			01234567890123456789
			<div id='pax.template.modifier.indent.example1'></div>
			[:.
				var myVars = { fruit: 'apples' };
				pax.template.render( "([" + ":= _indent( fruit, 7 ) :" + "])", myVars, pax.$('pax.template.modifier.indent.example1') );
			:]
		(end)
		This would show "(&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;apples)", ie: adding 7 spaces before the fruit.

	Example:
		(start code)
			01234567890123456789
			<div id='pax.template.modifier.indent.example2'></div>
			[:.
				var myVars = { fruit: 'apples' };
				pax.template.render( "([" + ":= _indent( fruit, 1, 'I like ' ) :" + "])", myVars, pax.$('pax.template.modifier.indent.example2') );
			:]
		(end)
		This would show "(I like apples)", ie: adding the specified string 1 time before the fruit.

*/
pax.template.modifier._indent = function( value, number, string ) {
	if( value == null )return value;
	string = ( typeof string != 'undefined' )? string : "&nbsp;";
	number = ( typeof number != 'undefined' )? number : 4;
	return ( new Array( number + 1 ).join( string ) ) + value;
};



/*	Method: _undent
	Removes extra whitespace characters from a multi-line string
	
	Parameters:
		value - The string to use
		tabSpaces - Optionally specify how many spaces per tab, default = 4

	Example:
		(start code)
			var myCode = "
				for( var x = 0; x < 10; x ++ ) {\
					for( var y = 0; y < 10; y ++ ) {\
						result += x * y;\
					}\
				}\
			";
			<div id='pax.template.modifier.undent.example1'></div>
			[:.
				var myVars = { code: myCode };
				pax.template.render( "<xmp>[" + ":= _undent( code ) :" + "]</xmp>", myVars, pax.$('pax.template.modifier.undent.example1') );
			:]
		(end)
		This would show the code in the div, un-indented

*/
pax.template.modifier._undent = function( value, tabSpaces ) {
	if( ! tabSpaces )tabSpaces = 4;
	var lines = value.split( '\n' );	//	Split by new line char
	
	//	Find smallest indent
	var indent = 100000;
	for( var i = 0; i < lines.length; i++ ) {
		var line = lines[i];
		var count = 0;
		if( line != '' ) {
			var useLine = false;								//	We don't count lines that contain just whitespace
			for( var x = 0; x < line.length; x++ ) {
				var c = line.charAt( x );
				if( c == ' ' )count += 1;
				else if( c == '\t' )count += tabSpaces;			//	tab = X spaces
				else if( c != '' && c.charCodeAt(0) != 13 ) {	//	Not empty and not new line
					useLine = true;
					break;
				}
			}
			if( count < indent && useLine ) {
				indent = count;
			}
		}
	}
	
	//	Remove smallest indent from all lines
	var newValue = '';
	for( var i = 0; i < lines.length; i++ ) {
		var line = lines[i];
		var count = 0;
		var newLine = '';
		for( var x = 0; x < line.length; x++ ) {
			var c = line.charAt( x );
			if( c == ' ' )count += 1;
			else if( c == '\t' )count += tabSpaces;
			//	We check if either the indentation is right, or if we have non-whitepsace chars.
			//	Note: we can't just use count >= indent, as that yields the whitespace char, 
			//	When tabs and spaces are mixed.
			if( count > indent || ( c != ' ' && c != '\t' ) )newLine += c;
		}
		newValue += '\n' + newLine;
	}
	
	return newValue;
};


/*	Method: _trim
	Returns string with whitespace removed rom both the front and end of a string
	
	Parameters:
		value - Which value to use

	Example:
		(start code)			
			<div id='pax.template.modifier.trim.example1'></div>
			[:.
				var myVars = { value: '		  This is some text that has whitespace befor and after the text		    ' };
				pax.template.render( "([" + ":= _trim( value ) :" + "])", myVars, pax.$('pax.template.modifier.trim.example1') );
			:]
		(end)
		This would show "(This is some text that has whitespace befor and after the text)", ie: removing whitespace before and after the text

*/
pax.template.modifier._trim = function( value ) {
	return value.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
};


/*	Method: _wrap
	Wraps a line *value* of text at *max*, using *newLine*, optionally cutting through words (*cutWords*).
	
	Parameters:
		value - the string to wrap
		max - where to wrap lines at in number of characters
		newLine - string to use for seperating lines, defaults to "<br>"
		cutWords - boolean to allow cutting of words, false by default

	Example:
		(start code)
			<div>0--------10--------20--------30--------40</div>
			<div>|123456789|123456789|123456789|123456789|</div>
			<div>'----'----'----'----'----'----'----'----'</div>
			<div id='pax.template.modifier.wrap.example1'></div>
			[:.
				var myVars = {
					text: 'Hi there, I am a line of text I need my words, such as supercalafragalisticexpialidocious wrapped asap!.',
					max: 20
				};
				pax.template.render( "[" + ":= _wrap( text, max ) :" + "]", myVars, pax.$('pax.template.modifier.wrap.example1') );
			:]
		(end)
		This would show the line wrapped at 20 chars, without cutting any long words.

	Example:
		(start code)
			<div>0--------10--------20--------30--------40--------50--------60--------70--------80--------90-------100-------110</div>
			<div>|123456789|123456789|123456789|123456789|123456789|123456789|123456789|123456789|123456789|123456789|123456789|</div>
			<div>'----'----'----'----'----'----'----'----'----'----'----'----'----'----'----'----'----'----'----'----'----'----'</div>
			<div id='pax.template.modifier.wrap.example2'></div>
			[:.
				var myVars = {
					text: 'Hi there, I am a line of text I need my words, such as supercalafragalisticexpialidocious wrapped asap!.',
					max: 20,
					newLine: '|'
				};
				pax.template.render( "[" + ":= _wrap( text, max, newLine ) :" + "]", myVars, pax.$('pax.template.modifier.wrap.example2') );
			:]
		(end)
		This would show the line seperated at 20 chars by the specified seperator "|", without cutting any long words.

	Example:
		(start code)
			<div>0--------10--------20</div>
			<div>|123456789|123456789|</div>
			<div>'----'----'----'----'</div>
			<div id='pax.template.modifier.wrap.example3'></div>
			[:.
				var myVars = {
					text: 'Hi there, I am a line of text I need my words, such as supercalafragalisticexpialidocious wrapped asap!.',
					max: 20,
					newLine: '<br' + '>',
					cutWords: true
				};
				pax.template.render( "[" + ":= _wrap( text, max, newLine, cutWords ) :" + "]", myVars, pax.$('pax.template.modifier.wrap.example3') );
			:]
		(end)
		This would show the line wrapped at 20 chars, but with words longer than 20 chars cut.

*/
pax.template.modifier._wrap = function( value, max, newLine, cutWords ) {
	if( value == null )return value;
	var seperator = " ";												// The seperator, usually space
	cutWords = (typeof(cutWords) != 'undefined') ? cutWords : false;	// Default to not cut the words
	newLine = ( typeof newLine != 'undefined' )? newLine : "<br>";
	var lines = value.split( newLine );
	if( max > 0 ) {
		for( var i = 0; i < lines.length; i++ ) {
			var line = lines[i];
			lines[i] = "";
			while( line.length > max ) {
				// Work out where to cut: it's at the last space if that occurs before max, otherwise it is max if the seperators is found, otherwise length of line.
				var cutAt = ( line.substring(0,max).lastIndexOf( seperator ) == -1 ) ? line.indexOf( seperator ) : line.substring(0,max).lastIndexOf( seperator ) + 1;
				if( cutAt == -1 )cutAt = line.length;
				if( cutAt < 1 || cutWords )cutAt = max;
				
				// Add the bits back into the line
				lines[i] += line.substr(0, cutAt);
				line = line.substr( cutAt );
				lines[i] += (line.length)? newLine : "";
			}
			lines[i] += line;
		}
	}
    return lines.join( newLine );
};